<template>
    <div id="App">
        <div class="content-box webrtc-test" v-show="targetClientId">
            <div class="video-container">
                <div class="video-panel" ref="screenRef" @mouseenter="mouseInHandle" @mouseleave="mouseOutHandle" @mousemove="mouseMoveHandle" @mousedown="handleClick">
                    <video ref="remoteVideo" autoplay playsinline></video>
                    <div class="message-panel" v-if="state.logVisible">
                        <div v-html="state.messageLog"></div>
                    </div>
                </div>
            </div>

            <div class="webrtc-header">
                <div class="top-btn align-center">
                    <el-button type="info" @click="state.logVisible = !state.logVisible">{{ state.logVisible ? '关闭日志' : '显示日志' }}</el-button>
                    <el-button type="danger" @click="stopScreenShare()">停止控制</el-button>
                </div>
            </div>

            <div class="status-panel">
                <div><h3>客户端: {{ targetClientId }}</h3></div>
                <div>信令: {{ state.signalingState }}</div>
                <div>ICE: {{ state.iceState }}</div>
                <div>连接: {{ state.connectionState }}</div>
                <div>视频流状态: {{ state.remoteStream ? '接收中' : '未接收' }}</div>
            </div>

            <div class="control-panel">
                <el-form label-width="120px" class="edit-form">
                    <el-form-item label="屏幕共享设置：">
                        <el-select v-model="state.selectedSourceIndex" @change="changeSourceIndex">
                            <el-option value="">选择屏幕源</el-option>
                            <el-option v-for="(source, key) in state.remoteSources" :key="source.id" :value="key" :label="`${source.name} ${source.label}`"></el-option>
                        </el-select>
                    </el-form-item>
                </el-form>
            </div>
        </div>

        <div class="container" v-show="!targetClientId">
            <Nav v-if="!$route.meta.hideNav"/>
            <router-view/>
        </div>
    </div>
</template>

<script setup>
import {onMounted, onUnmounted, provide, reactive, ref, watch} from 'vue';
import {ElMessage} from "element-plus";
import Nav from "./Nav.vue";

defineOptions({
    name: 'App'
});

const targetClientId = ref("");
const controlById = ref("");

const remoteVideo = ref(null);

const state = reactive({
    logVisible: true,
    mouseIN: false,
    iceState: '',
    connectionState: '',
    messageLog: '',

    localStream: null,
    remoteStream: null,

    selectedSourceIndex: 0,

    signalingServer: '',
    signalingState: 'disconnected',
    sources: [],
    remoteSources: [],
    userId: window.APP_CONFIG.user_id,
});

// WebRTC 相关
let peerConnection = null,
    commandConn = null,
    dataChannel = null,
    signalingConn;

const screenRef = ref(null);
const heartbeatInterval = 15000; // 心跳检测间隔

// 链接本地后台鼠标键盘控制接口
const connectCommand = () => {
    if (commandConn) {
        commandConn.close();
        commandConn = null;
    }
    commandConn = new WebSocket(`ws://${import.meta.env.VITE_API_URL}/control-self?userId=${state.userId}`);
    commandConn.onopen = () => {
        console.log("命令通道打开")
    };

    commandConn.onmessage = (event) => {
        if (event.data !== "pong"){
            console.log("命令通道接收", event.data);
        }
    };

    commandConn.onclose = () => {
        console.log("命令通道 disconnected")
    };

    commandConn.onerror = (error) => {
        console.log(`命令通道 onerror${error.error.code}`)
    };
}


// 计算实际点击位置
const getRealPosition = (offsetX, offsetY) => {
    let divWidth = screenRef.value.getBoundingClientRect().width,
        divHeight = screenRef.value.getBoundingClientRect().height,
        screen = state.remoteSources[state.selectedSourceIndex];
    let x = parseInt(screen.width * offsetX / divWidth),
        y = parseInt(screen.height * offsetY / divHeight);
    return {x: screen.nativeOrigin.x + x, y: screen.nativeOrigin.y + y};
}

// 鼠标点击事件
const handleClick = (event) => {
    if (!state.mouseIN) return
    // 0 左键 1中键  2右键
    if (event.button === 2) {
        event.preventDefault();
    }
    let pos = getRealPosition(event.offsetX, event.offsetY);
    console.log('点击位置:', event.offsetX, event.offsetY, '实际位置:', pos);
    sendDataChannelMsg({type: "mouseclick", key: event.button == 0 ? "left" : "right", x: pos.x, y: pos.y});
}

// 键盘控制
function handleKeyDown(event) {
    if (!state.mouseIN) return;
    let key = event.key.toLowerCase();
    let keyMsg = {type: 'keyboard', key: key};
    if (key !== 'f12' && key !== 'f5') event.preventDefault();
    const keyData = {
        key: key,
        modifiers: {
            ctrl: event.ctrlKey,
            alt: event.altKey,
            shift: event.shiftKey,
            meta: event.metaKey
        }
    };
    sendDataChannelMsg(keyMsg)
}

// 鼠标进入
const mouseInHandle = () => {
    state.mouseIN = true;
    document.addEventListener('keydown', handleKeyDown);
}

// 鼠标退出
const mouseOutHandle = () => {
    state.mouseIN = false;
    document.removeEventListener('keydown', handleKeyDown);
}

// 鼠标移动
const mouseMoveHandle = (event) => {
    if (!state.mouseIN) return
    let pos = getRealPosition(event.offsetX, event.offsetY);
    sendDataChannelMsg({type: "mousemove", x: pos.x, y: pos.y});
}

// 同步客户端状态
const syncStatus = (form) => {
    sendSignalingMessage({
        type: 'status',
        data: {
            user_id: window.APP_CONFIG.user_id,
            preview_img: state.sources[0].thumbnail,
            screens: state.sources,
            ...form
        },
        from: window.APP_CONFIG.user_id,
        to: "",
    });
}

// 信令服务器连接
const connectSignaling = async () => {
    try {
        let myClientId = window.APP_CONFIG.user_id;
        console.log('window.APP_CONFIG.user_id', window.APP_CONFIG.user_id)
        if (!myClientId) return

        console.log('server_url_is:', state.signalingServer)
        signalingConn = new WebSocket(state.signalingServer);

        signalingConn.onopen = () => {
            state.signalingState = 'connected';
            logMessage('connectSignaling success');
            syncStatus({room: "", control_by: "", control_flag: true})
        };

        signalingConn.onmessage = (event) => {
            if (event.data === "pong") return; //忽略心跳消息
            handleSignalingMessage(JSON.parse(event.data))
        };

        signalingConn.onclose = () => {
            state.signalingState = 'disconnected';
            logMessage('connectSignaling disconnect');
        };

        signalingConn.onerror = (error) => {
            logMessage(`[${state.signalingServer}] connectSignalingErr: ${error.error.code}`);
        };

        return signalingConn;
    } catch (error) {
        logMessage(`connectSignaling connectFail: ${error.message}`);
    }
};

const disconnectSignaling = () => {
    if (signalingConn) {
        signalingConn.close();
        signalingConn = null;
    }
};

// 发送信令消息
const sendSignalingMessage = (msg) => {
    let message = typeof msg === 'string' ? JSON.parse(msg) : msg;
    message.from = state.userId;
    if (signalingConn && signalingConn.readyState === WebSocket.OPEN) {
        // signalingConn.send(JSON.stringify(message));
        signalingConn.send(JSON.stringify(message));
        logMessage(`sendSignalingMessage: ${message.type}`);
    }
};


// 心跳检测状态
function startHeartbeat() {
    setInterval(() => {
        if (!signalingConn || ![WebSocket.OPEN, WebSocket.CONNECTING].includes(signalingConn.readyState)) { // 如果断开连接，发起重连
            logMessage("sendSignalingMessage reconnect", signalingConn);
            connectSignaling();
        } else if (signalingConn && signalingConn.readyState === WebSocket.OPEN) {
            signalingConn.send("ping"); // 心跳保持
        }
        if (!commandConn || ![WebSocket.OPEN, WebSocket.CONNECTING].includes(commandConn.readyState)) {
            connectCommand();
        } else if (commandConn && commandConn.readyState === WebSocket.OPEN) {
            commandConn.send("ping"); // 心跳保持
        }
    }, heartbeatInterval);
}


/*信令服务先关部分 END*/


/*PeerConnection部分*/

const changeSourceIndex = (v) => {
    if (v === "") return
    sendDataChannelMsg({type: "screen", key: v});
}

const changeScreen = (v) => {
    console.log('改变视频', v, state.localStream);
    stopScreenShare();
    startBeControl(controlById.value);
}


const setVideoTrack = () => {
    // 添加本地流
    console.log('添加本地流', peerConnection, state.localStream)
    if (state.localStream && peerConnection) {
        state.localStream.getTracks().forEach(track => {
            console.log('推送本地流', state.localStream)
            peerConnection.addTrack(track, state.localStream);
        });
    }
}

const startBeControl = async (to) => {
    controlById.value = to;
    await startScreenShare();
    // 初始化 WebRTC 连接
    console.log('start方设置peerConnection')
    initializePeerConnection(to);
    // 视频通道
    setVideoTrack();
    //数据通道
    dataChannel = peerConnection.createDataChannel("myDataChannel");
    console.log("数据通道已创建:", dataChannel)
    handleDataChannel();
    //发送ready信号
    sendSignalingMessage({
        type: 'ready',
        data: "",
        to: to,
    });

    setTimeout(function () {
        createOffer(to)
    }, 2000)
}

// 处理信令消息
const handleSignalingMessage = async (message) => {
    logMessage(`收到消息: ${message.type}`);

    switch (message.type) {
        case 'offer':
            logMessage('收到 Offer，可以创建 Answer');
            await createAnswer(message.data, message.to, message.from);
            break;

        case 'answer':
            logMessage('收到 Answer，可以设置到连接中');
            await addAnswer(message.data);
            break;

        case 'ice':
            if (peerConnection) {
                await peerConnection.addIceCandidate(new RTCIceCandidate(message.data));
            }
            break;
        case 'start':
            await startBeControl(message.from);
            break;
        case 'ready':
            console.log('ready方设置peerConnection')
            initializePeerConnection(message.from);
            break;
    }
};


// 获取屏幕源
const refreshSources = async () => {
    try {
        if (window.electronAPI) {
            // [{id: 'screen:0:0', name: '屏幕 1', thumbnail: '…i3W53Pvx3yf+1DnZHT5z/B10xoDJ24FLgAAAAAElFTkSuQmCC', labe: '', width, height},
            // {id: 'window:329302:0', name: 'go – webrtc.vue', thumbnail: '…CIMi+90/j4OAYOhwOx/8H8neuxANIAMsAAAAASUVORK5CYII='}]
            state.sources = await window.electronAPI.getSources({
                types: ['screen'],
                thumbnailSize: {width: 320, height: 180}
            });
            console.log('所有屏幕信息', state.sources)
            logMessage(`找到 ${state.sources.length} 个屏幕源`);
        }
    } catch (error) {
        logMessage(`获取屏幕源失败: ${error.message}`);
    }
};

const startClient = (user) => {
    targetClientId.value = user.user_id;
    state.remoteSources = user.screens;
    console.log('startClient', user.user_id)
    sendSignalingMessage({
        type: 'start',
        data: "",
        to: user.user_id,
    });
}

// 开始屏幕共享
const startScreenShare = async () => {
    try {
        // 获取屏幕流
        const stream = await navigator.mediaDevices.getUserMedia({
            audio: false,
            video: {
                mandatory: {
                    chromeMediaSource: 'desktop',
                    chromeMediaSourceId: state.sources[state.selectedSourceIndex].id,
                    minWidth: 1280,
                    // minHeight: 720,
                    maxWidth: 1920,
                    // maxHeight: 1080,
                    minFrameRate: 1,
                    maxFrameRate: 30
                }
            }
        });
        state.localStream = stream;
        return stream
        logMessage('屏幕共享已开始');
    } catch (error) {
        logMessage(`屏幕共享失败: ${error.message}`);
    }
};

const stopScreenShare = () => {
    targetClientId.value = "";
    controlById.value = "";
    state.messageLog = "";

    if (dataChannel) {
        dataChannel.close();
        dataChannel = null;
    }

    if (state.localStream) {
        state.localStream.getTracks().forEach(track => track.stop());
        state.localStream = null;
    }
    if (state.remoteStream) {
        remoteVideo.value = null;
        state.remoteStream.getTracks().forEach(track => track.stop());
        state.remoteStream = null;
    }
    if (peerConnection) {
        peerConnection.close();
        peerConnection = null;
    }

    logMessage('屏幕共享已停止');
};

// 处理数据通道消息
const handleDataChannelMsg = (msg) => {
    let data = JSON.parse(msg);
    if (data.type === 'screen') {
        changeScreen(parseInt(data.key));
    } else {
        commandConn.send(msg);
    }
}

// 发送数据通道消息
const sendDataChannelMsg = (msg) => {
    if (dataChannel && state.connectionState === 'connected'){
        dataChannel.send(typeof msg === 'string' ? msg : JSON.stringify(msg));
    }
}

// 处理数据通道
const handleDataChannel = () => {
    // 设置数据通道回调
    dataChannel.onopen = () => {
        console.log('Data channel opened!');
    };
    dataChannel.onmessage = (event) => {
        console.log('Data channel Received message:', typeof event.data, event.data);
        // 处理接收到的消息
        handleDataChannelMsg(event.data);
    };
    dataChannel.onclose = () => {
        console.log('Data channel closed');
    };
    dataChannel.onerror = (error) => {
        console.error('Data channel error:', error);
    };
}

// 初始化 Peer Connection
const initializePeerConnection = (to) => {
    const configuration = {
        // iceTransportPolicy: "relay",
        iceServers: [
            {urls: 'stun:stun.l.google.com:19302'},
            {urls: 'stun:stun1.l.google.com:19302'},
            {urls: 'stun:stun2.l.google.com:19302'},
            {urls: 'stun:stun3.l.google.com:19302'},
            {urls: 'stun:stun4.l.google.com:19302'},
            // {
            //     urls: "turn:127.0.0.1:3478", //101.37.81.144
            //     username: "user1",
            //     credential: "password123123",
            // }
        ]
    };

    peerConnection = new RTCPeerConnection(configuration);
    console.log('设置RTCPeerConnection', peerConnection)

    // 监听远程流
    peerConnection.ontrack = (event) => {
        logMessage('收到远程流');
        console.log('打印视频流', event.streams[0])
        state.remoteStream = event.streams[0];
        if (remoteVideo.value) {
            logMessage('----添加到video视频播放----');
            remoteVideo.value.srcObject = state.remoteStream;
        }
    };

    // ICE 连接状态
    peerConnection.oniceconnectionstatechange = () => {
        state.iceState = peerConnection.iceConnectionState;
        logMessage(`ICE 状态: ${peerConnection.iceConnectionState}`);
    };

    peerConnection.onconnectionstatechange = () => {
        state.connectionState = peerConnection.connectionState;
        logMessage(`连接状态: ${peerConnection.connectionState}`);
        if (['disconnected', 'closed', 'failed'].includes(peerConnection.connectionState)) {
            stopScreenShare();
        }
    };

    peerConnection.ondatachannel = (event) => {
        dataChannel = event.channel;
        console.log("数据通道已接收:", dataChannel)
        handleDataChannel();
    };

    // ICE 候选
    peerConnection.onicecandidate = (event) => {
        if (event.candidate) {
            console.log("ICE 候选:", event.candidate.candidate, "类型:", event.candidate.type); // host/srflx/relay
            sendSignalingMessage({
                type: 'ice',
                data: event.candidate,
                to: to,
            });
        }
    };
};

// 创建 Offer
const createOffer = async (to) => {
    if (!peerConnection) {
        logMessage('请先开始屏幕共享');
        return;
    }

    try {
        const offer = await peerConnection.createOffer({
            offerToReceiveAudio: true,
            offerToReceiveVideo: true
        });
        await peerConnection.setLocalDescription(offer);

        sendSignalingMessage({
            type: 'offer',
            to: to,
            data: offer,
        });

        logMessage('Offer 已创建并发送');
    } catch (error) {
        logMessage(`创建 Offer 失败: ${error.message}`);
    }
};

// 创建 Answer
const createAnswer = async (offer, from, to) => {
    if (!peerConnection || !offer) {
        logMessage('需要先收到 Offer');
        return false;
    }
    try {
        await peerConnection.setRemoteDescription(new RTCSessionDescription(offer));
        const answer = await peerConnection.createAnswer();
        await peerConnection.setLocalDescription(answer);
        sendSignalingMessage({
            type: 'answer',
            data: answer,
            from: from,
            to: to
        });
        logMessage('Answer 已创建并发送');
        return true;
    } catch (error) {
        logMessage(`创建 Answer 失败: ${error.message}`);
        return false;
    }
};

// 设置 Answer
const addAnswer = async (answer) => {
    if (!peerConnection || !answer) {
        logMessage('需要先收到 Answer');
        return false;
    }
    try {
        await peerConnection.setRemoteDescription(new RTCSessionDescription(answer));
        logMessage('Answer 已设置');
        return true
    } catch (error) {
        logMessage(`设置 Answer 失败: ${error.message}`);
        return false;
    }
};

// 日志记录
const logMessage = (message) => {
    const timestamp = new Date().toLocaleTimeString();
    let line = `[${timestamp}] ${message}`;
    console.log(line);
    state.messageLog = `${line}<br>` + state.messageLog;
};

provide(
    "startClient", startClient,
    "stopScreenShare", stopScreenShare,
);

watch(() => controlById.value, (newValue, oldValue) => {
    syncStatus({room: "", control_by: newValue, control_flag: true})
})

// 生命周期
onMounted(async () => {
    if (!window.APP_CONFIG.user_id || !window.APP_CONFIG.server_url) return ElMessage.error('未配置信令服务器地址或者用户ID');
    state.signalingServer = `ws://${window.APP_CONFIG.server_url}/ws?client=${window.APP_CONFIG.user_id}`;
    await refreshSources();
    logMessage('WebRTC 测试工具已就绪');
    await connectSignaling();
    connectCommand();
    startHeartbeat();

});

onUnmounted(() => {
    stopScreenShare();
});
</script>

<style scoped>
.webrtc-test {
    max-width: 100%;
    margin: 0 auto;
    font-family: "Microsoft YaHei";
    padding: 0 !important;
}

.webrtc-header {
    .top-btn {
        font-size: 14px;
    }
}


.video-container {
    text-align: center;
    margin-bottom: 10px;
}

.video-panel {
    text-align: center;
    width: 100%;
    position: relative;
    font-size: 0;
    cursor: none;
    overflow: hidden;
}

.video-panel video {
    width: 100%;
    height: auto;
    background: #000;
    border: 1px solid #ddd;
}

.status-panel {
    margin-bottom: 20px;
    border: 1px solid #ddd;
    border-radius: 8px;
    background: #f9f9f9;
    font-size: 12px;
    padding: 2px 5px;
    display: flex;
    align-items: center;
    justify-content: space-between;

    > div {
        flex: 1
    }
}

.message-panel {
    padding: 5px;
    position: absolute;
    pointer-events: none; /*屏蔽点击事件*/
    top: 0;
    left: 0;
    text-align: left;
    width: 20%;
    height: 60%;
    overflow: hidden;
    background: rgba(0, 0, 0, 0.5);
    font-size: 12px;
    color: #8df102;
    opacity: 0.6;
    border-radius: 0 6px 6px 0;
}

</style>